//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.northpool.operator.statistics.dataset;

import com.alibaba.fastjson.JSONObject;
import com.northpool.operator.annotation.InputParam;
import com.northpool.operator.annotation.InputParams;
import com.northpool.operator.annotation.OperatorInfo;
import com.northpool.operator.annotation.OutputParam;
import com.northpool.operator.annotation.OutputParams;
import com.northpool.operator.common.database.DatabaseUtils;
import com.northpool.operator.param.ParamTool;
import com.northpool.operator.param.custom.DatasetParam;
import com.northpool.operator.type.TypeEnum;
import com.northpool.operator.utils.StpOperatorHelper;
import com.northpool.resources.datasource.IDataSource;
import com.northpool.resources.datatable.bean.TableMetaStatistics;
import com.northpool.resources.datatable.operate.IColumn;
import com.northpool.resources.datatable.operate.ITableOperator;
import com.northpool.spatial.Geom;
import com.northpool.spatial.grid.Constants;
import com.northpool.spatial.grid.GridManager;
import com.northpool.spatial.grid.QuadtreeGrid;
import com.northpool.spatial.grid.extent.GridExtent;
import com.northpool.spatial.utils.GeomUtils;
import com.northpool.stpclient.bean.task.response.WrapperTableMetaStatistics;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@OperatorInfo(
        ref = "StatisticalDatasetMeta",
        name = "描述性统计"
)
@OutputParams({
        @OutputParam(ref = "statisticalResults", name = "统计结果", type = TypeEnum.STRING)
})
public class StatisticalDatasetMeta extends StpOperatorHelper {
    private static final int MAX_LEVEL_TILE_COUNT = 50_0000;

    @InputParams({
            @InputParam(ref = "dataset", name = "数据集", desc = "数据集包含数据源ID和数据表名称的JSON描述，如: {\"dataSourceId\":\"xxx-xxx\",\"tableName\":\"table\"}", type = TypeEnum.DB),
            @InputParam(ref = "hasTileExtent", name = "是否包含瓦片范围", desc = "是否包含瓦片范围", type = TypeEnum.BOOL, required = false, value = "false")
    })
    public StatisticalDatasetMeta() {
    }

    @Override
    public void execute(ParamTool params) throws Exception {
        this.log("开始执行统计任务...");
        DatasetParam dataset = params.getDataset("dataset");
        String dataSourceId = dataset.getDataSourceId();
        String tableName = dataset.getTableName();

        String dataSourceStr = DatabaseUtils.getDataSourceJson(dataSourceId);
        Boolean hasTileExtent = params.getBool("hasTileExtent");
        IDataSource datasource = null;
        try {
            datasource = IDataSource.fromJSON(dataSourceStr);
            ITableOperator tableOperator = datasource.getTableOperator(tableName);
            this.log("统计表数据信息...");
            TableMetaStatistics tableMetaStatistics = tableOperator.getMetaStatistics(hasTileExtent);
            WrapperTableMetaStatistics metaStatistics = new WrapperTableMetaStatistics();
            metaStatistics.fromTableMetaStatistics(tableMetaStatistics);
            metaStatistics.setExecutionTime(new Date());
            metaStatistics.setDataSourceId(datasource.getId());
            metaStatistics.setTableName(tableName);

            LinkedHashMap<String, Integer> levelCountMap = this.buildLevelMap(metaStatistics, tableOperator);

            metaStatistics.setTileLevelCountMap(levelCountMap);

            String metaStr = JSONObject.toJSONString(metaStatistics, true);
            this.result("statisticalResults", metaStr);
            datasource.destroy();
            this.successExit();
        } catch (Exception ex) {
            ex.printStackTrace();
            if (datasource != null) {
                datasource.destroy();
            }
            this.error(ex.getMessage());
            this.failedExit();
        }
    }

    private LinkedHashMap<String, Integer> buildLevelMap(WrapperTableMetaStatistics metaStatistics, ITableOperator tableOperator) {
        LinkedHashMap<String, Integer> levelCountMap = new LinkedHashMap<>();
        String tileExtentWkt = metaStatistics.getTileExtent();
        if (StringUtils.isBlank(tileExtentWkt)) {
            return null;
        }
        Map<String, IColumn> columnMap = tableOperator.getColumnMap();
        String spatialName = tableOperator.getSpatialColumn();

        IColumn spatialColumn = columnMap.get(spatialName);
        Integer srid = spatialColumn.getSRID();
        Constants.GRID_UNIT gridUnit = Constants.GRID_UNIT.getGridUnitBySrid(srid);
        QuadtreeGrid grid = GridManager.getQuadtreeGrid(gridUnit, Constants.GRID_BASE.base512, Constants.GRID_TYPE.tdt);

        double[] extent = metaStatistics.getExtent();
        Geom geom = new Geom(com.northpool.spatial.Constants.SPATIAL_TYPE.wkt, tileExtentWkt);
        geom.changeType(com.northpool.spatial.Constants.SPATIAL_TYPE.esriGeometry);
        int beginLevel = grid.getBeginLevel(), endLevel = 30;
        List<GridExtent> extentsByBBox = grid.getGridExtentsByBBox(beginLevel, extent[0], extent[1], extent[2], extent[3]);
        Map<String, Boolean> isCoverMap = new ConcurrentHashMap<>();
        for (int i = beginLevel; i <= endLevel; i++) {
            List<GridExtent> nextLevelExtents = Collections.synchronizedList(new ArrayList<>());
            // 先按照四至范围裁
            AtomicInteger count = new AtomicInteger();
            extentsByBBox.parallelStream().forEach(item -> {
                if (item == null) return;
                Geom geometryByExtent = GeomUtils.getGeometryByExtent(item);
                String parentKey = String.format("%s_%s_%s", item.getLevel() - 1, item.getX() >> 1, item.getY() >> 1);
                if (isCoverMap.containsKey(parentKey)) {
                    count.incrementAndGet();
                    GridExtent[] children = item.getChildren();
                    Collections.addAll(nextLevelExtents, children);
                    for (GridExtent child : children) {
                        isCoverMap.put(child.getKey(), true);
                    }
                } else if (GeomUtils.intersects(geometryByExtent, geom)) {
                    count.incrementAndGet();
                    Collections.addAll(nextLevelExtents, item.getChildren());
                    if (GeomUtils.coveredBy(geometryByExtent, geom)) {
                        isCoverMap.put(item.getKey(), true);
                    }
                }
            });
            levelCountMap.put(String.valueOf(i), count.get());
            extentsByBBox.clear();
            extentsByBBox.addAll(nextLevelExtents);
            if (count.get() > MAX_LEVEL_TILE_COUNT) {
                break;
            }
        }
        return levelCountMap;
    }
}
